#define DadePixelSampling

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using CoreCalc;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data;
using RayDen.Library.Data.Imaging;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;

//using RayDen.RayEngine.Engines.ProgressivePhotonMapping;
using RayDen.RayEngine.Engines.ProgressivePhotonMapping;

namespace RayDen.RayEngine
{


    using RayDen.RayEngine.Data;

    public enum LayerBlendOptions
    {
        Override,
        Sum,
        Mul,
        Interpolation,
    }


    public abstract class ImageFilmBase : SamplerAggregator, Core.Interface.IImageFilm
    {
        public static int GAMMA_TABLE_SIZE = 1024;

        public static int SampleBufferSize = 4096;

        public static float filterSize = 1.2f;
        private static int filterTableSize = 4;
        public bool SplatMode = GlobalConfiguration.Instance.Get("Film.SplatMode", false);

        public float linearToneMapValue = GlobalConfiguration.Instance.LinearToneMapValue;

        public bool Filter = GlobalConfiguration.Instance.FilterImage;

        public bool ToneMap = GlobalConfiguration.Instance.ToneMapping;

        public int FilterType = GlobalConfiguration.Instance.FilterType;


        protected Filter filter;

        protected FilterLUTs filterLUTs;

        public ToneMapParams toneMap;

        public ToneMapOperator ToneMapping;


        protected internal SamplePixel[] pixelData;

        protected Dictionary<int, SamplePixel[]> layers;

        protected SamplePixel[] statsImage;

        protected FrameBuffer frameBuffer;

        public int Width;

        public int Height;


        int IImageFilm.Width
        {
            get { return Width; }
        }

        int IImageFilm.Height
        {
            get { return this.Height; }
        }

        public long statsTotalSampleCount;

        protected SampleBuffer Samples;

        internal float[] gammaTable;

        protected List<SampleBuffer> sampleBuffers;
        protected ConcurrentQueue<SampleBuffer> freeSampleBuffers;

        public ImageFilmBase(int width, int height)
        {
            this.Width = width;
            this.Height = height;
            this.Samples = new SampleBuffer((SampleBufferSize));
            this.frameBuffer = new FrameBuffer(width, height);
            this.InitGammaTable();
            this.pixelData = new SamplePixel[width * height];
            this.statsImage = new SamplePixel[width * height];
            //RgbSpectrum.Gamma = true;
            if (this.ToneMap)
            {
                switch (GlobalConfiguration.Instance.ToneMapType)
                {
                    case 1:
                        float scale = float.Parse(GlobalConfiguration.Instance.ToneMapParams);
                        ToneMapping = new LinearToneMap(this, scale);
                        break;
                    case 2:
                        ToneMapping = new ReinhardToneMap(this);
                        break;
                    case 3:
                        ToneMapping = new QuadricToneMap(this);
                        break;
                }

                this.toneMap = new Reinhard02ToneMapParams();
                //new LinearToneMapParams(linearToneMapValue);
            }

            if (this.Filter)
            {
                switch (FilterType)
                {
                    case 0:
                        filter = new BoxFilter(1.2f, 1.2f);
                        break;
                    case 1:
                        this.filter = new GaussianFilter(filterSize, filterSize);
                        break;
                    case 2:
                        this.filter = new MitchelFilter(filterSize, filterSize);
                        break;
                    default:
                        filter = new BoxFilter(1.2f, 1.2f);
                        break;
                }
                Tracer.TraceLine("Image filter used : {0}", filter.GetType().Name);
                if (filter == null)
                {
                    this.filter =
                        //new BoxFilter(1.2f, 1.2f);
                        new GaussianFilter(filterSize, filterSize);
                }
                this.filterLUTs = new FilterLUTs(this.filter, filterTableSize);
            }

            this.sampleBuffers = new List<SampleBuffer>();
            this.freeSampleBuffers = new ConcurrentQueue<SampleBuffer>();
        }

        public override void Reset()
        {
            lock (lclock)
            {
                for (int i = 0; i < pixelData.Length; i++)
                {
                    pixelData[i] = new SamplePixel();
                }
                this.Samples.Reset();
                this.frameBuffer.Clear();
            }
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Merge(ImageFilmBase b)
        {
            if (b.pixelData.Length != pixelData.Length)
            {
                throw new ArgumentException("Invalid film size");
            }

            for (int index = 0; index < b.pixelData.Length; index++)
            {
                var pixelA = pixelData[index];
                var pixelB = b.pixelData[index];


                pixelData[index] = new SamplePixel()
                {
                    Radiance = (pixelA.Radiance + pixelB.Radiance) * 0.5f,
                    Weight = (pixelA.Weight + pixelB.Weight) * 0.5f,
                };
            }
        }

        public void ResetProcessor()
        {
            this.Reset();
        }

        protected bool IsPreviewOver()
        {
            return true;
        }

        private readonly object lclock = new object();

        public virtual SampleBuffer GetFreeSampleBuffer()
        {
            lock (lclock)
            {

                //          boost::mutex::scoped_lock lock(splatMutex);

                // Look for a free buffer
                if (freeSampleBuffers.Count > 0)
                {
                    SampleBuffer sb;
                    freeSampleBuffers.TryDequeue(out sb);
                    sb.Reset();
                    return sb;
                }
                else
                {
                    // Need to allocate a new buffer
                    var sb = new SampleBuffer(SampleBufferSize);
                    sampleBuffers.Add(sb);
                    return sb;
                }
            }
        }


        public void FreeSampleBuffer(SampleBuffer sampleBuffer)
        {
            lock (lclock)
            {
                freeSampleBuffers.Enqueue(sampleBuffer);
            }
        }

        public void SplatSampleBuffer(bool isPreviewOver, SampleBuffer sampleBuffer)
        {
            lock (lclock)
            {
                var sbe = sampleBuffer;
                if (this.Filter)
                {
                    for (var i = 0; i < sampleBuffer.GetSampleCount(); ++i)
                    {
                        this.SplatFiltered(sampleBuffer.Samples[i]);
                    }
                }
                else
                {
                    for (var i = 0; i < sampleBuffer.GetSampleCount(); ++i)
                    {
                        SampleBufferElem sampleElem = sbe.Samples[i];
                        int x = (int)sampleElem.screenX;
                        int y = (int)sampleElem.screenY;
                        this.SplatRadiance(ref sampleElem.radiance, x, y, 1.0f);
                    }
                }
                //                statsTotalSampleTime += WallClockTime() - t;
                //statsTotalSamplesCount += sampleBuffer->GetSampleCount();

                freeSampleBuffers.Enqueue(sampleBuffer);
            }
        }

        protected void SplatFiltered(SampleBufferElem sampleElem)
        {
            var dx = (sampleElem.screenX + 0.5f);
            var dy = (sampleElem.screenY + 0.5f);
            var fLUT = this.filterLUTs.GetLUT(dx - (float)Math.Floor(sampleElem.screenX),
                dy - (float)Math.Floor(sampleElem.screenY));
            var lut = fLUT.GetLUT();




            int x0 = MathLab.Ceil2UInt(dx - this.filter.xWidth);
            int x1 = x0 + fLUT.GetWidth();
            int y0 = MathLab.Ceil2UInt(dy - this.filter.yWidth);
            int y1 = y0 + fLUT.GetHeight();
            int lutOffset = 0;
            for (int iy = y0; iy < y1; ++iy)
            {
                if (iy < 0)
                {
                    lutOffset += fLUT.GetWidth();
                    continue;
                }
                else if (iy >= (this.Height))
                    break;

                for (int ix = x0; ix < x1; ++ix)
                {
                    float filterWt = lut[lutOffset++];

                    if ((ix < 0) || (ix >= (this.Width)))
                        continue;
                    this.SplatRadiance(ref sampleElem.radiance, ix, iy, filterWt);
                }
            }
        }

        public void SetGamma(float gamma = 2.2f)
        {
            float x = 0f;
            float dx = 1f / GAMMA_TABLE_SIZE;
            for (int i = 0; i < GAMMA_TABLE_SIZE; ++i, x += dx)
                this.gammaTable[i] = (float)Math.Pow(MathLab.Clamp(x, 0f, 1f), 1f / gamma);
        }

        internal void InitGammaTable()
        {
            float x = 0f;
            this.gammaTable = new float[GAMMA_TABLE_SIZE];
            float dx = 1f / GAMMA_TABLE_SIZE;
            for (int i = 0; i < GAMMA_TABLE_SIZE; ++i, x += dx)
                this.gammaTable[i] = (float)Math.Pow(MathLab.Clamp(x, 0f, 1f), 1f / 2.2f);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal float Radiance2PixelFloat(float x)
        {

            // Very slow !
            //return powf(Clamp(x, 0.f, 1.f), 1.f / 2.2f);

            int index = Math.Min(
                MathLab.Floor2UInt(GAMMA_TABLE_SIZE * MathLab.Clamp(x, 0f, 1f)),
                GAMMA_TABLE_SIZE - 1);
            return this.gammaTable[index];
        }


        protected virtual void UpdateFrameBuffer()
        {
#if VERBOSE
            try
            {
#endif
            var pixelCount = this.Width * this.Height;
            var sampledPixels = this.pixelData;
            var pix = new RgbSpectrum();
            if (!this.ToneMap)
            {
                //lock (lclock)

                for (int i = 0; i < pixelCount; ++i)
                {
                    if (this.SplatMode)
                    {
                        if (pixelData[i].Weight <= 0f)
                            continue;
                        pix = new RgbSpectrum(this.Radiance2PixelFloat(pixelData[i].Radiance.c1),
                            this.Radiance2PixelFloat(pixelData[i].Radiance.c2),
                            this.Radiance2PixelFloat(pixelData[i].Radiance.c3));
                        this.frameBuffer.AddPixel(i, ref pix);
                    }
                    else
                    {
                        ComputePixel(i);
                    }
                }


            }
            else if (ToneMapping != null)
            {
                ToneMapping.ComputeImage();
            }
                if (false && this.toneMap != null)
            {
                switch (this.toneMap.GetToneMapType())
                {
                    case ToneMapType.TONEMAP_LINEAR:
                        var tmparam = (LinearToneMapParams)this.toneMap;
                        for (var j = 0; j < pixelCount; ++j)
                        {
                            if (this.SplatMode)
                            {
                                RgbSpectrum ppx = RgbSpectrum.ZeroSpectrum();
                                SSE.ComputePixel(ref sampledPixels[j].Radiance, tmparam.scale, ref ppx, gammaTable,
                                    GAMMA_TABLE_SIZE);
                                this.SetPixel(j, ref pix);
                            }
                            else
                            {
                                if (!sampledPixels[j].Radiance.IsBlack())
                                {
                                    ComputePixel(j, tmparam.scale);
                                }

                            }
                        }
                        break;
                    case ToneMapType.TONEMAP_REINHARD02:
                        float alpha = .9f;
                        var tm = new Reinhard02ToneMapParams();
                        //(Reinhard02ToneMapParams)this.toneMap;
                        float preScale = tm.preScale;
                        float postScale = tm.postScale;
                        float burn = tm.burn;
                        var xyzPixels = new SamplePixel[sampledPixels.Length];
                        float Ywa = 0f;
                        for (int ii = 0; ii < pixelCount; ++ii)
                        {
                            float weight = sampledPixels[ii].Weight;

                            if (weight > 0f)
                            {
                                float invWeight = 1f / weight;

                                RgbSpectrum rgb = sampledPixels[ii].Radiance * invWeight;

                                rgb.XYZ(out xyzPixels[ii].Radiance.c1, out xyzPixels[ii].Radiance.c2,
                                    out xyzPixels[ii].Radiance.c3);
                                /*
                            // Convert to XYZ color space
                            xyzPixels[ii].Radiance.c1 = 0.412453f * rgb.c1 + 0.357580f * rgb.c2 + 0.180423f * rgb.c3;
                            xyzPixels[ii].Radiance.c2 = 0.212671f * rgb.c1 + 0.715160f * rgb.c2 + 0.072169f * rgb.c3;
                            xyzPixels[ii].Radiance.c3 = 0.019334f * rgb.c1 + 0.119193f * rgb.c2 + 0.950227f * rgb.c3;
                             */
                                xyzPixels[ii].Weight = weight;

                                // var xyz = pixelsRadiance[i].XYZ();

                                Ywa += xyzPixels[ii].Radiance.c2;

                            }
                        }
                        Ywa /= pixelCount;

                        // Avoid division by zero
                        if (Ywa.NearEqual(0f))
                            Ywa = 1f;

                        float Yw = preScale * alpha * burn;
                        float invY2 = 1f / (Yw * Yw);
                        float pScale = postScale * preScale * alpha / Ywa;

                        for (int ii = 0; ii < pixelCount; ++ii)
                        {
                            var xyz = xyzPixels[ii].Radiance;

                            float ys = xyz.c2;
                            xyz *= pScale * (1f + ys * invY2) / (1f + ys);
                            //xyz.GammaCorrection();
                            xyz.XYZToRGB();
                            /*
                        // Convert back to RGB color space
                        sampledPixels[ii].Radiance.c1 = 3.240479f * xyz.c1 - 1.537150f * xyz.c2 - 0.498535f * xyz.c3;
                        sampledPixels[ii].Radiance.c2 = -0.969256f * xyz.c1 + 1.875991f * xyz.c2 + 0.041556f * xyz.c3;
                        sampledPixels[ii].Radiance.c3 = 0.055648f * xyz.c1 - 0.204043f * xyz.c2 + 1.057311f * xyz.c3;
                            
                        this.pixelData[ii].Radiance = sampledPixels[ii].Radiance;
                         */
                            //pixelData[ii].Weight = 1.0f;

                            /*
                        this.frameBuffer.SetPixel(ii, this.Radiance2PixelFloat(sampledPixels[ii].radiance.c1),
                                                 this.Radiance2PixelFloat(sampledPixels[ii].radiance.c2),
                                                     * this.Radiance2PixelFloat(sampledPixels[ii].radiance.c3));*/
                            //this.SetPixel(ii, ref xyz);

                            this.ComputePixel(ii, ref xyz, 1.0f);
                            /*
                        pix = new RgbSpectrum(
                                      this.Radiance2PixelFloat(sampledPixels[ii].Radiance.c1),
                                      this.Radiance2PixelFloat(sampledPixels[ii].Radiance.c2),
                                      this.Radiance2PixelFloat(sampledPixels[ii].Radiance.c3));
                        this.SetPixel(ii, ref pix);*/
                        }

                        break;
                }
            }

#if VERBOSE
            }
            catch (Exception ex)
            {
                Tracer.TraceLine(ex.Message);
                throw ex;
            }
            
#endif

            //ClearPixelData();
        }



        protected internal virtual void ComputePixel(int i, float w = 1.0f)
        {
            //RgbSpectrum.Gamma = true;

            float weight = pixelData[i].Weight;
            if (weight > 0f)//&& !pixelData[i].Radiance.IsBlack())
            {
                var f = w / weight;

#if !DadePixelSampling
                var pix = pixelData[i].Radiance*f;
                pix.XYZToRGB();
                this.SetPixel(i, ref pix);
                
                /*float[] rgb;
                SampledSpectrum.XYZToRGB((pixelData[i].Radiance*f).ToArray(), out rgb);

                var pix = new RgbSpectrum(rgb);
                */
#else
                /*
                var pix = new RgbSpectrum
                    {
                        c1 = Radiance2PixelFloat(pixelData[i].Radiance.c1 * (f)),
                        c2 = Radiance2PixelFloat(pixelData[i].Radiance.c2 * (f)),
                        c3 = Radiance2PixelFloat(pixelData[i].Radiance.c3 * (f))

                    };*/

                var ppx = new RgbSpectrum();
                SSE.ComputePixel(ref pixelData[i].Radiance, f, ref ppx, gammaTable, GAMMA_TABLE_SIZE);
                this.SetPixel(i, ref ppx);

#endif
            }
        }


        protected internal void ComputePixel(int i, ref RgbSpectrum val, float pw, float w = 1.0f)
        {
            //RgbSpectrum.Gamma = true;

            float weight = pw;
            if (weight > 0f)
            {
                var f = w / weight;
                /*
                var pix = new RgbSpectrum
                {
                    c1 = Radiance2PixelFloat(val.c1 * (f)),
                    c2 = Radiance2PixelFloat(val.c2 * (f)),
                    c3 = Radiance2PixelFloat(val.c3 * (f))

                };*/

                RgbSpectrum ppx = RgbSpectrum.ZeroSpectrum();
                SSE.ComputePixel(ref val, f, ref ppx, gammaTable, GAMMA_TABLE_SIZE);
                this.SetPixel(i, ref ppx);

                //var pix = (f*pixelData[i].Radiance).ToRgb();
                //this.SetPixel(i, ref pix);
            }
        }
        /*
        public static void SampleHitPoint(SampleBuffer sampleBuffer, PPMHitPoint hp, int photonTraced) {
            switch (hp.type) {
                case HitPointType.Constant: {
                        sampleBuffer.SplatSample(hp.scrX, hp.scrY, hp.throughput);
                        break;
                    }
                case HitPointType.Surface: {
                        if (hp.accumPhotonCount == 0) {
                            sampleBuffer.SplatSample(hp.scrX, hp.scrY, new RgbSpectrum());
                        }
                        else {
                            float k = 1.0f / (MathLab.M_PI * hp.photonRadius2 * photonTraced);
                            if (hp.accumDirectLightRadiance.IsBlack()) {
                                var rad = hp.accumReflectedFlux * k;
                                sampleBuffer.SplatSample(hp.scrX, hp.scrY, rad.ToRgb());
                            }
                            //else 
                            {
                                sampleBuffer.SplatSample(hp.scrX, hp.scrY, ((hp.accumReflectedFlux * k) + hp.accumDirectLightRadiance) / hp.accumPhotonCount);
                            }
                        }
                        break;
                    }
            }
        }
        */
        public override void UpdateScreenBuffer()
        {
            //lock (lclock)
            {
                //this.ClearPixelData();
                this.UpdateFrameBuffer();
                //frameBuffer.Clear();
            }
        }

        private void ClearPixelData()
        {
            this.pixelData = new SamplePixel[Width * Height];
        }

        public override sealed float[] GetScreenBuffer()
        {
            return this.GetPixelData();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected internal void SetPixel(int p0, ref RgbSpectrum pix)
        {
            this.frameBuffer.SetPixel(p0, ref pix);
        }

        protected virtual void SplatRadiance(ref RgbSpectrum radiance, int x, int y, float weight = 1.0f)
        {
            //int offset = x + y * width;
            this.AddPixel(x, y, ref radiance, weight);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        protected void AddPixel(int x, int y, ref RgbSpectrum r, float w, SamplePixel[] pixelD = null)
        {
            //var offset = x + (Height - 1 - y) * Width;
            var offset = x + (y) * this.Width;
            if (pixelD == null)
            {
                pixelD = pixelData;
            }

            if (offset >= this.pixelData.Length)
                return;
            if (offset < 0)
                return;
            /*Parallel._AtomicAdd(ref pixels[offset].radiance.r, r.r);
            Parallel._AtomicAdd(ref pixels[offset].radiance.g, r.g);
            Parallel._AtomicAdd(ref pixels[offset].radiance.b, r.b);
            Parallel._AtomicAdd(ref pixels[offset].weight, w);
            */
            //            var c = new RgbSpectrum(r.XYZ());
#if !DadePixelSampling
            //r.ToXYZ();
#endif
            pixelD[offset].Radiance.Add(ref r);
            pixelD[offset].Weight += w;
            //r.IsBlack() ? 0f : w;
            var i = r;
            //this.frameBuffer.SetPixel(offset, ref i);

        }
        protected void AddPixel(int offset, ref RgbSpectrum r, float w)
        {

            if (offset >= this.pixelData.Length)
                return;
            if (offset < 0)
                return;
            /*Parallel._AtomicAdd(ref pixels[offset].radiance.r, r.r);
            Parallel._AtomicAdd(ref pixels[offset].radiance.g, r.g);
            Parallel._AtomicAdd(ref pixels[offset].radiance.b, r.b);
            Parallel._AtomicAdd(ref pixels[offset].weight, w);
            */
            this.pixelData[offset].Radiance += r;
            this.pixelData[offset].Weight += w;//+= w;
            var i = this.pixelData[offset].Radiance;
            //this.frameBuffer.SetPixel(offset, ref i);
        }

        public virtual string GetStats()
        {
            return string.Empty;
        }

        public virtual void SavePNG(string fileName, string[] watermark)
        {
            lock (lclock)
            {
                this.UpdateFrameBuffer();
            }

            //RgbSpectrum.Gamma = false;
            using (Bitmap img = new Bitmap(this.Width, this.Height))
            {
                //BitmapData data = img.LockBits(new Rectangle(0, 0, Width, Height),
                //ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
                // unsafe {
                //byte* ptr = (byte*)data.Scan0.ToPointer();
                //int ImgOffset = data.Stride - Width * 3;
                var pixelData = this.GetPixelData();
                var nonz = Enumerable.Count<float>(pixelData, c => c > 0f);
                for (int y = 0; y < this.Height; y++)
                {
                    for (int x = 0; x < this.Width; x++
                        //,ptr += 3
                        )
                    {
                        var ii = nonz + 1;
                        var offset = (x + y * this.Width) * 3;

                        //                            int offset = 3 * (x + (height - y - 1) * width);
#if SPECTRUM
                        img.SetPixel(x, y, new RgbSpectrum(pixelData[offset], pixelData[offset + 1], pixelData[offset + 2]).ToColor());
#else

                        img.SetPixel(x, Height - y - 1, new RgbSpectrum(pixelData[offset], pixelData[offset + 1], pixelData[offset + 2]).ToColor());
#endif
                        //float weight = pixelWeights[x + (height - y - 1) * width];
                        //if (weight != 0)
                        //    weight = 1f/weight;
                        //var clr = (weight*pixelsRadiance[x + (height - y - 1)*width]).Transform();
                        //byte r = clr.R;
                        //byte g = clr.G;
                        //byte b = clr.B;

                        ////byte r = (byte)(255f * pixels[offset] + .5f);
                        ////byte g = (byte)(255f * pixels[offset + 1] + .5f);
                        ////byte b = (byte)(255f * pixels[offset + 2] + .5f);
                        //ptr[2] = r;
                        //ptr[1] = g;
                        //ptr[0] = b;
                    }
                    //    ptr += ImgOffset;
                }
                // }
                //    img.UnlockBits(data);
                //if (statsToRender != null)
                //{
                //    using (Graphics g = Graphics.FromImage(img))
                //    {
                //        using (var fn = new Font(FontFamily.GenericSansSerif, 9.5f))
                //        {
                //            for (int i = 0; i < statsToRender.Length; i++)
                //            {
                //                g.DrawString(statsToRender[i], fn, Brushes.White, 10, 550 + 12 * i);
                //            }
                //        }
                //    }
                //}
                ImageFactory.ApplyWaterMark(10, Height - (watermark.Length * 24), img, watermark);
                img.Save(fileName);
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public float[] GetPixelData()
        {
            return this.frameBuffer.GetPixelData();
        }

        public virtual void SaveRaw(string fn)
        {
            var rawData = new ArrayFile<RgbSpectrum>(this.pixelData.Select(item => item.Weight.NearZero() ? RgbSpectrum.ZeroSpectrum() : item.Radiance / item.Weight).ToArray());
            rawData.Save(fn + ".raw");
            ImageFactory.FreeImageSaveExrBitmap(fn, Width, Height, this.pixelData.Select(item => item.Weight > 0f ? item.Radiance / item.Weight : RgbSpectrum.ZeroSpectrum()).ToArray());

            //ImageFactory.FreeImageSaveExrBitmap(fn, Width, Height, this.frameBuffer.GetPixelData().ToSpectrumList().ToArray());
        }


        public void SplatHitPoints(List<PPMHitPoint> hitPoints, PPMContext engine)
        {
            Tracer.TraceLine("Start splatting hitpoints");
            var sb = GetFreeSampleBuffer();
            int nonZero = 0;
            /*
            Parallel.For(0, hitPoints.Count, index =>
            {
                var ppmHitPoint = hitPoints[index];
                if (ppmHitPoint.accumPhotonCount > 0)
                {
                    //Debugger.Break();
                    nonZero++;
                }
                sb.SampleHitPoint(ppmHitPoint, engine.PhotonMapPhotons);

                if (sb.IsFull())
                {
                    SplatSampleBuffer(true, sb);
                    FreeSampleBuffer(sb);
                    sb = GetFreeSampleBuffer();
                }
            });
            */
            for (int index = 0; index < hitPoints.Count; index++)
            {
                var ppmHitPoint = hitPoints[index];
                if (ppmHitPoint.accumPhotonCount > 0)
                {
                    //Debugger.Break();
                    nonZero++;
                }
                sb.SampleHitPoint(ppmHitPoint, engine.PhotonsGathered);

                if (sb.IsFull())
                {
                    SplatSampleBuffer(true, sb);
                    FreeSampleBuffer(sb);
                    sb = GetFreeSampleBuffer();
                }
            }

            Tracer.TraceLine("Non zero splatted hitpoints {0}", nonZero);
        }


    }


    public class DefaultImageFilm : ImageFilmBase
    {
        public DefaultImageFilm(int width, int height)
            : base(width, height)
        {
        }

        public override void Consume(PathSamplerBase path)
        {

        }
    }

}